<template>
  <div class="vote-h5-container">
    <!-- 5. 新增：v-if="showMainContent" 控制主体内容（非微信环境隐藏） -->
    <!-- 1. 全屏加载层：覆盖整个屏幕，优先级最高 -->
    <div class="fullscreen-loading" v-if="isLoading">
      <van-loading color="#333" size="40px" />
      <!-- 可选：添加加载文本 -->
      <div class="loading-text">加载中...</div>
    </div>
    <!-- 背景音乐 -->
    <!-- 2. 背景音乐控制按钮：左上角绝对定位 -->
    <div
      v-if="activityInfo && activityInfo.backgroundMusic"
      class="music-control"
      @click="toggleMusic"
    >
      <van-icon
        :name="isMusicPlaying ? 'music-o' : 'play-circle-o'"
        size="24"
        color="#fff"
      />
    </div>
    <div
      v-if="showMainContent && activityInfo && currentConfig"
      class="page-content"
    >
      <!-- 公告 -->
      <div class="custom-notice-bar" v-if="activityInfo.activityNotice">
        <van-notice-bar
          left-icon="volume-o"
          :text="activityInfo.activityNotice"
        />
      </div>

      <!-- 漂浮物容器 - 覆盖整个区域 -->
      <!-- <div class="floating-container" v-if="floatingItems.length > 0">
        <div
          class="floating-item"
          v-for="(item, index) in floatingItems"
          :key="item.id"
          :style="getFloatingStyle(item)"
        >
          <img :src="item.src" :alt="`漂浮物${index}`" class="floating-img" />
        </div>
      </div> -->
      <!-- 中间内容区（轮播、统计、列表等） -->
      <div class="main-content" :style="{ background: mainAreaBg }">
        <!-- 轮播图 -->
        <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
          <van-swipe-item
            v-for="(slide, idx) in carouselImages"
            :key="`carousel-${idx}`"
          >
            <div class="carousel-slide">
              <img
                :src="slide.image"
                class="slide-image"
                :alt="`轮播图${idx + 1}`"
              />
            </div>
          </van-swipe-item>
        </van-swipe>
        <!-- <img
                src='../assets/banner_san.webp'
              /> -->
        <!-- 信息统计 -->
        <div class="infoCount">
          <div
            class="stats"
            :style="{
              background: currentConfig.countBg,
              color: currentConfig.count,
            }"
          >
            <div class="stat-item">
              <div class="stat-value">{{ activityInfo.participantCount }}</div>
              <div class="stat-label">演员数</div>
            </div>
            <div class="stat-item">
              <div class="stat-value">{{ activityInfo.totalVotes }}</div>
              <div class="stat-label">总投票</div>
            </div>
            <div class="stat-item">
              <div class="stat-value">{{ activityInfo.viewCount }}</div>
              <div class="stat-label">浏览量</div>
            </div>
          </div>
          <div class="countdown">
            距离活动结束：<span :style="{ color: commonColor }"
              >{{ remainingTime.days }} 天 {{ remainingTime.hours }} 时
              {{ remainingTime.minutes }} 分
              {{ remainingTime.seconds }} 秒</span
            >
          </div>
        </div>
        <!-- 时间信息 -->
        <div class="time-info">
          <div class="time_item">
            <van-icon name="clock-o" style="margin-right: 5px" />开始时间：{{
              activityInfo.startTime
            }}
          </div>
          <div class="time_item">
            <van-icon name="underway-o" style="margin-right: 5px" />结束时间：{{
              activityInfo.endTime
            }}
          </div>
          <div class="time_item">
            <van-icon
              name="notes-o"
              style="margin-right: 5px"
            />一个投票者每天能投 {{ everyDay.dailyVoteLimit }} 票
          </div>
        </div>
        <!-- 报名按钮 -->
        <div class="BaoMing_Btn">
          <van-button
            style="width: 100%"
            :style="buttonStyle"
            :color="commonColor"
            @click="showPerPopup"
            >我要报名</van-button
          >
        </div>
        <!-- 搜索栏 -->
        <div class="search-container">
          <van-search
            style="background-color: #fff !important"
            v-model="page.keyword"
            placeholder="姓名/编号"
            show-action
            @search="onSearch"
          >
            <template #action>
              <div @click="onSearch" class="search_btn" :style="buttonStyle">
                搜索
              </div>
            </template>
          </van-search>
        </div>
        <!-- 演员列表 -->
        <div class="player-list-section">
          <van-list
            v-model="loading"
            :finished="finished"
            finished-text="没有更多了"
            @load="onLoad"
            :error.sync="error"
            error-text="请求失败，点击重新加载"
            style="padding-bottom: 20px"
          >
            <div
              class="players-container"
              :class="listDisplayMode"
              v-if="list.length > 0"
            >
              <div
                class="player-item"
                v-for="(item, i) in list"
                :key="`${item.id}-${i}`"
              >
                <div class="player-rank">
                  {{ item.activityNum }}
                </div>
                <div class="player-avatar-container">
                  <van-image
                    round
                    width="70px"
                    height="70px"
                    :src="
                      item.imageUrl && item.imageUrl.trim()
                        ? item.imageUrl
                        : require('@/assets/list_touxiang.png')
                    "
                  />
                  <!-- <div class="online-indicator" v-if="i % 3 === 0"></div> -->
                </div>
                <div class="player-info">
                  <div class="player-name">{{ item.name }}</div>
                  <div class="player-votes">
                    <van-icon name="like" color="#ee0a24" />
                    <span>{{ item.voteCount }}</span> 票
                  </div>
                </div>
                <van-button
                  round
                  type="primary"
                  size="small"
                  :style="buttonStyle"
                  @click="voteForPlayer(item)"
                >
                  投票
                </van-button>
              </div>
            </div>
            <!-- <div class="empty-state" v-if="list.length === 0 && !emptyState">
              <van-empty description="暂无演员数据" />
            </div> -->
          </van-list>
        </div>
      </div>

      <!-- 弹窗组件（报名、详情、加票） -->
      <signUp
        :childrenData="passPerData"
        @child-event="handleChildEvent"
      ></signUp>
      <PlayerDetails
        :childrenData="passPlayerData"
        @add-event="handleAddEvent"
        @child-event="handleChildEvent"
      ></PlayerDetails>
      <AddTickets
        :childrenAddData="passAddData"
        @AddTickets-event="AddTicketsEvent"
      ></AddTickets>
    </div>
  </div>
</template>
<script>
// 获取assets静态资源
const getAssetsImg = (path) => {
  return new URL(path, import.meta.url).href;
};
import axios from "axios";
import wx from "weixin-js-sdk";
const QQMapWX = require("../utils/map/qqmap-wx-jssdk.min.js");
import urlencode from "urlencode";
import AMapLoader from "@amap/amap-jsapi-loader";
import { activeInfo, playerList, getPlayerDetail } from "../api/index.js";
import { Toast, Dialog } from "vant";
import Qs from "qs";
import AMapUtil from "@/utils/amap";
import { text } from "@/utils/text"; //服务协议
const isWechat = () => {
  const ua = navigator.userAgent.toLowerCase();
  return ua.includes("micromessenger") && !ua.includes("windows wechat"); // 排除电脑微信
};
import signUp from "./signUpModule/signUp.vue"; //报名
import AddTickets from "../view/AddTickets.vue";
import PlayerDetails from "./PlayerModule/PlayerDetails.vue"; //投票
import voteService from "@/utils/voteService"; // 引入投票服务

import { styleConfig } from "@/utils/voteH5StyleConfigs.js"; // 引入新的样式配置文件
export default {
  name: "VoteH5",
  components: {
    signUp,
    PlayerDetails,
    AddTickets,
  },
  data() {
    return {
      audio: null, // 音频实例
      isMusicPlaying: false, // 播放状态
      isAutoPlayAttempted: false, // 标记是否已尝试过自动播放（避免重复提示）

      // 2. 新增：控制主体内容是否显示（非微信环境隐藏）
      showMainContent: true,
      timestamp: "", //生成签名的时间戳
      nonceStr: "", //生成签名的随机串
      signature: "", //签名
      code: "",
      isWechat: isWechat, // 绑定函数到实例
      bookedPerson: "",
      show: true,
      userInfo: "/wx/info", //微信获取用户信息,
      openid: "/wx/getUrl", //微信获取opedId,
      jssdk: "/wx/jssk", //微信获取签名 /wx/jssk
      token: "/wx/getToken", //微信获取token

      // 新增：投票相关状态
      currentUser: {
        openId: "",
        hasUserInfo: false,
        nickname: "",
        avatar: "",
      },
      loginRetryCount: 0, // 登录重试次数
      isLoggingIn: false, // 防止重复登录

      activeTab: 0,
      searchText: "",
      sortMethod: 0,
      showSignUp: false,

      passPerData: {
        show: false,
        buttonStyle: "",
        activeInfo: {},
      },
      passPlayerData: {
        passCurrentConfig: {},
        show: false,
        playerItem: {},
      },
      passAddData: {
        passCurrentConfig: {},
        show: false,
        playerItem: {},
      },
      signUpForm: {
        name: "",
        phone: "",
        declaration: "",
      },
      sortOptions: [
        { text: "按编号排序", value: 0 },
        { text: "按票数排序", value: 1 },
        { text: "按时间排序", value: 2 },
      ],
      navItems: [
        { text: "首页", icon: "home-o", to: "/" },
        { text: "排行", icon: "bar-chart-o", to: "/ranking" },
        { text: "报名", icon: "edit", to: "/signup" },
        { text: "帮助", icon: "question-o", to: "/help" },
      ],
      // 缺少以下两个关键参数的初始化！
      page: {
        keyword: "",
        page: 1,
        pageSize: 10,
      },
      total: 0, // 总数据条数（从接口获取）
      list: [],
      loading: false,
      finished: false,
      error: false, //提示错误信息
      emptyState: false, //初始空状态

      participantCount: null, // 演员数
      totalVotes: null, // 总投票
      viewCount: null, // 浏览量
      startTime: "",
      endTime: "",
      // 新增：剩余时间相关
      remainingTime: {
        days: 0,
        hours: 0,
        minutes: 0,
        seconds: 0,
      },
      timer: null, // 定时器ID
      activityStatus: "", // 活动状态：pending(未开始)/active(进行中)/ended(已结束)

      templateId: 1, // 设置一个默认的模板ID
      activityInfo: null,
      isLoading: true, // 新增 加载状态
      activityId: "", // 新增：存储动态提取的activityId
      listDisplayMode: "card-mode", //默认  grid list card
      everyDay: null, // 一个投票者每天投多少票
    };
  },
  computed: {
    floatingItems() {
      // 确保 floatingElements 是数组（兜底）
      const floatingElements = this.activityInfo?.floatingElements || [];
      if (!Array.isArray(floatingElements)) return [];

      // 过滤出激活的漂浮物（active: true 且有图片链接）
      const activeFloating = floatingElements.filter(
        (item) => item.active && item.src
      );

      // 生成多个随机实例
      const items = [];
      activeFloating.forEach((floating, index) => {
        // 每个漂浮物生成 3-5 个实例
        const count = 3 + Math.floor(Math.random() * 3);
        for (let i = 0; i < count; i++) {
          items.push({
            id: `float-${index}-${i}`, // 唯一ID
            src: floating.src,
            startX: 15 + Math.random() * 70, // 水平位置（15%-85%）
            duration: 8 + Math.random() * 8, // 下落时长（8-16秒）
            delay: Math.random() * 5, // 延迟（0-5秒）
            size: 0.5 + Math.random() * 0.5, // 大小（0.5-1倍）
          });
        }
      });

      return items;
    },
    currentConfig() {
      // 若templateId未获取到（接口未返回），返回空对象，页面不渲染
      if (!this.templateId || !styleConfig[this.templateId]) {
        return {};
      }
      // 接口返回templateId后，匹配对应的样式
      return styleConfig[this.templateId];
    },
    headerStyle() {
      return this.currentConfig.header;
    },
    mainAreaBg() {
      return this.currentConfig.mainAreaBg;
    },
    buttonStyle() {
      return this.currentConfig.button;
    },
    titleColor() {
      return this.currentConfig.titleColor;
    },
    commonColor() {
      return this.currentConfig.commonColor;
    },
    footerBg() {
      return this.currentConfig.footerBg;
    },
    navColor() {
      return this.currentConfig.navColor;
    },
    navActiveColor() {
      return this.currentConfig.navActiveColor;
    },
    currentSlides() {
      return this.currentConfig.slides;
    },

    // 新增：合并 mainCover 和 additionalImages 为轮播图数据
    carouselImages() {
      if (!this.activityInfo) return [];

      // 处理 additionalImages（不变）
      let additionalImgs = [];
      try {
        additionalImgs = JSON.parse(this.activityInfo.additionalImages || "[]");
        additionalImgs = additionalImgs.filter((img) => img?.trim());
      } catch (error) {
        console.error("解析 additionalImages 失败:", error);
        additionalImgs = [];
      }

      // 处理 mainCover（核心修改：统一路径+日志）
      const mainCoverImg = this.activityInfo.mainCover?.trim();
      let mainImg = [];
      console.log("🚀 ~ carouselImages ~ mainCoverImg:", mainCoverImg);
      if (mainCoverImg) {
        try {
          // 提取文件名（假设 mainCoverImg 是 "/assets/banner_ping.webp"）
          const fileName = mainCoverImg.split("/").pop(); // 得到 "banner_ping.webp"
          // 固定目录 @/assets，仅动态传入文件名
          const imgPath = require(`@/assets/${fileName}`);
          mainImg = [imgPath];
          console.log("mainCover 加载成功");
        } catch (error) {
          console.error("mainCover 加载失败，改用默认图:", error);
          mainImg = [require("@/assets/banner_green.webp")];
        }
      }

      // 合并图片（mainCover+additionalImages）
      const allImgs = [...mainImg, ...additionalImgs].filter((img) => img);
      console.log("🚀 ~ carouselImages ~ allImgs:", allImgs);

      // 最终容错：无图时用默认图
      if (allImgs.length === 0) {
        return [
          { image: require("@/assets/banner_ping.webp") }, // 统一用 @ 绝对路径
        ];
      }

      return allImgs.map((img) => ({ image: img }));
    },
  },
  watch: {
    // 当配置变化时，重新发送
    // currentConfig(newVal) {
    //   this.$emit("update-config", newVal);
    // },
    // 监听背景音乐链接变化（应对异步加载场景）
    "activityInfo.backgroundMusic"(newVal) {
      if (newVal) {
        this.initAudio(true); // true 表示可能需要自动播放
      } else {
        this.destroyAudio(); // 无链接时清理音频
      }
    },
    currentConfig(newVal) {
      if (newVal && this.activityInfo) {
        this.$emit("update-activity-info", {
          activityInfo: this.activityInfo,
          currentConfig: newVal,
        });
      }
    },
    templateId(newVal, oldVal) {
      console.log(`模板ID从${oldVal}变为${newVal}，样式已更新`);
    },
  },
  created() {
    // 调用提取方法
    this.extractActivityId();
  },
  mounted() {
    // 新增：页面加载完成后，若有音乐链接则尝试自动播放
    if (this.activityInfo && this.activityInfo.backgroundMusic) {
      this.initAudio(true); // true → 触发自动播放
    }
    // 并行获取活动详情和演员列表
    // 2. 提取到activityId后，再请求数据
    if (this.activityId) {
      this.fetchInitialData();
    } else {
      // 容错：提取失败时用默认ID，避免页面卡死
      this.activityId = "dd165ff653324d859c03323bed86590c";
      this.fetchInitialData();
    }

    const targetParam = this.getUrlParam();
    console.log("获取到的参数值:", targetParam);
    // 当组件挂载后，向父组件(App.vue)发送配置数据
    // this.$emit("update-config", this.currentConfig);
    // 是否是微信状态
    if (!this.isWechat()) {
      this.show = false; // 关闭加载状态
      this.showMainContent = false; // 隐藏主体内容
      this.showWechatTip(); // 显示微信提示框
      return; // 终止后续微信相关逻辑（避免授权报错）
    }

    // 检查URL中是否有用户信息授权的回调
    const urlParams = this.getUrlCode();
    if (urlParams.code && urlParams.state === "userinfo") {
      // 处理用户信息授权回调
      this.handleUserInfoCallback(urlParams.code).then(() => {
        // 清除URL中的参数
        this.clearWechatCode();
        // 继续正常的登录流程
        this.show = false;
        this.authorizeLogin();
      });
      return;
    }

    // 原有微信环境逻辑（仅微信环境执行）
    this.show = false;
    this.authorizeLogin();
  },
  beforeDestroy() {
    // 清除剩余时间定时器
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }

    // 新增：组件销毁时清理音频，避免内存泄漏
    this.destroyAudio();
  },
  methods: {
    // 生成漂浮物动态样式（绑定到每个漂浮物）
    getFloatingStyle(item) {
      return {
        left: `${item.startX}%`, // 水平位置
        transform: `scale(${item.size})`, // 缩放大小
        "--swing-range": `${item.swing}px`, // 自定义CSS变量（摇摆幅度）
        animationDuration: `${item.duration}s`, // 动画时长
        animationDelay: `${item.delay}s`, // 动画延迟
      };
    },
    // 1. 初始化音频（新增 autoPlay 参数，控制是否自动播放）
    initAudio(autoPlay = false) {
      const musicUrl = this.activityInfo?.backgroundMusic;
      if (!musicUrl) return;

      // 已创建实例则先销毁，避免重复创建
      if (this.audio) {
        this.destroyAudio();
      }

      // 创建音频实例，设置循环播放
      this.audio = new Audio(musicUrl);
      this.audio.loop = true; // 循环播放（避免播一次就停）

      // 监听音频事件，同步播放状态
      this.audio.addEventListener("play", () => {
        this.isMusicPlaying = true;
      });
      this.audio.addEventListener("pause", () => {
        this.isMusicPlaying = false;
      });
      this.audio.addEventListener("error", (err) => {
        console.error("音频加载失败:", err);
        this.$toast.fail("背景音乐加载失败，请检查链接");
        this.destroyAudio(); // 加载失败则清理实例
      });

      // 尝试自动播放（仅当 autoPlay 为 true 且未尝试过）
      if (autoPlay && !this.isAutoPlayAttempted) {
        this.audio
          .play()
          .then(() => {
            // 自动播放成功
            this.isMusicPlaying = true;
            this.isAutoPlayAttempted = true;
          })
          .catch((err) => {
            // 自动播放被浏览器阻止（常见场景）
            console.warn("自动播放被拦截，需用户点击触发:", err);
            this.isAutoPlayAttempted = true;
            this.isMusicPlaying = false;
            // 提示用户手动点击播放
            this.$toast({
              message: "点击右上角音乐图标播放背景音乐",
              duration: 3000,
              position: "bottom",
            });
          });
      }
    },

    // 2. 销毁音频实例（避免内存泄漏）
    destroyAudio() {
      if (this.audio) {
        this.audio.pause();
        this.audio.removeEventListener("play", () => {});
        this.audio.removeEventListener("pause", () => {});
        this.audio.removeEventListener("error", () => {});
        this.audio = null;
      }
      this.isMusicPlaying = false;
      this.isAutoPlayAttempted = false; // 重置自动播放尝试标记
    },

    // 3. 原有切换播放方法（保留，支持用户点击控制）
    toggleMusic() {
      if (!this.audio) {
        this.initAudio(); // 未初始化则先创建实例
        return;
      }

      if (this.isMusicPlaying) {
        this.audio.pause();
      } else {
        this.audio.play().catch((err) => {
          console.error("播放失败:", err);
          this.$toast.fail("音乐播放失败，请稍后重试");
        });
      }
      this.isMusicPlaying = !this.isMusicPlaying;
    },
    // 新增：轮播图图片加载失败时，替换为默认图
    handleCarouselImgError(event, idx) {
      console.warn(`轮播图${idx + 1}加载失败，使用默认图`);
      // 替换为默认图路径
      event.target.src = require("@/assets/banner_green.webp");
    },
    extractActivityId() {
      try {
        const currentUrl = window.location.href;
        const urlObj = new URL(currentUrl);
        console.log("本地无 activityId，从 URL 提取:", currentUrl);

        // 分割路径，定位 /h5/ 后的片段
        const pathSegments = urlObj.pathname.split("/");
        const validSegments = pathSegments.filter(
          (segment) => segment.trim() !== ""
        );

        // 找到 "h5" 后的片段（ID 所在位置）
        const h5Index = validSegments.findIndex((segment) => segment === "h5");
        let activityIdWithExtra = "";
        if (h5Index !== -1 && validSegments[h5Index + 1]) {
          activityIdWithExtra = validSegments[h5Index + 1];
        }

        // 核心：截断可能的参数（&之后的内容），取纯 ID
        const pureActivityId = activityIdWithExtra.split("&")[0].trim();
        console.log("截断参数后的原始 ID:", pureActivityId);

        // 严格验证：必须是 32 位字母数字组合（符合 UUID 格式）
        const isValid =
          pureActivityId.length === 32 && /^[0-9a-fA-F]+$/.test(pureActivityId);
        if (isValid) {
          this.activityId = pureActivityId;
          localStorage.setItem("vote_activityId", this.activityId); // 存入本地，下次复用
          console.log(
            "成功提取 32 位 activityId 并存储到本地:",
            this.activityId
          );
        } else {
          // 提取失败（非 32 位或格式错误），用默认值兜底
          this.activityId = "dd165ff653324d859c03323bed86590c";
          localStorage.setItem("vote_activityId", this.activityId);
          console.warn(
            `提取的 ID(${pureActivityId})不是 32 位字母数字，使用默认值:`
          );
        }
      } catch (error) {
        console.error("提取 activityId 失败:", error);
        // 异常时兜底
        this.activityId = "dd165ff653324d859c03323bed86590c";
        localStorage.setItem("vote_activityId", this.activityId);
      }
    },
    // 获取活动信息
    async fetchInitialData() {
      try {
        // 显示统一加载提示
        this.$toast.loading({
          message: "加载中...",
          forbidClick: true,
          duration: 0,
        });

        if (!this.activityId) {
          this.$toast.fail("未找到活动ID");
          this.isLoading = false;
          return;
        }

        // 只请求活动详情接口，移除演员列表接口的并行请求
        const activityResponse = await activeInfo(this.activityId);

        // 处理活动详情响应
        console.log(
          "🚀 ~ fetchInitialData ~ activityResponse:",
          activityResponse
        );
        if (activityResponse.data && activityResponse.data.code === 0) {
          console.log(
            "🚀 ~ fetchInitialData ~ this.activityInfo:",
            this.activityInfo
          );
          this.activityInfo = activityResponse.data.data;
          this.everyDay = JSON.parse(this.activityInfo.voteSettings);
          this.startTime = activityResponse.data.data.startTime; //2025-09-02 00:00:00
          this.endTime = activityResponse.data.data.endTime; //2025-10-28 00:00:00
          // const time_yu  计算剩余时间
          // 关键：初始化剩余时间计算
          this.initRemainingTime();

          // 关键：设置接口返回的templateId，触发currentConfig更新
          this.templateId = this.activityInfo.templateId;
          this.listDisplayMode = this.activityInfo.listDisplayMode;
          // 数据获取后再传递给父组件
          this.$emit("update-activity-info", {
            activityInfo: this.activityInfo,
            currentConfig: this.currentConfig,
          });
          // 存储到 localStorage（兜底：页面刷新后不丢失）

          localStorage.setItem("vote_templateId", this.templateId.toString()); // 转字符串存储
        } else {
          this.$toast.fail(activityResponse.data?.msg || "获取活动信息失败");
          // 异常时兜底存储
          localStorage.setItem("vote_activityId", this.activityId);
          localStorage.setItem("vote_templateId", "1"); // 默认模板ID
        }
        this.isLoading = false;
      } catch (error) {
        console.error("初始数据加载失败:", error);
        this.$toast.fail("网络异常，数据加载失败");
        this.isLoading = false;
        // 错误时兜底存储
        localStorage.setItem("vote_activityId", this.activityId);
        localStorage.setItem("vote_templateId", "1");
      } finally {
        this.isLoading = false; // 结束加载
        // 关闭加载提示
        this.$toast.clear();
      }
    },
    // 解析时间字符串为Date对象（处理"2025-09-02 00:00:00"格式）
    parseTime(timeStr) {
      const [datePart, timePart] = timeStr.split(" ");
      const [year, month, day] = datePart.split("-").map(Number);
      const [hours, minutes, seconds] = timePart.split(":").map(Number);
      // 注意：月份是0-based（0-11），所以要减1
      return new Date(year, month - 1, day, hours, minutes, seconds);
    },

    // 计算剩余时间并更新状态
    calculateRemainingTime() {
      const now = new Date();
      const start = this.parseTime(this.startTime);
      const end = this.parseTime(this.endTime);

      // 判断活动状态
      if (now < start) {
        this.activityStatus = "pending";
        this.remainingTime = { days: 0, hours: 0, minutes: 0, seconds: 0 };
      } else if (now > end) {
        this.activityStatus = "ended";
        this.remainingTime = { days: 0, hours: 0, minutes: 0, seconds: 0 };
        // 活动结束后清除定时器
        if (this.timer) {
          clearInterval(this.timer);
          this.timer = null;
        }
      } else {
        this.activityStatus = "active";
        // 计算剩余毫秒数
        const remainingMs = end - now;
        // 转换为天、时、分、秒
        this.remainingTime = {
          days: Math.floor(remainingMs / (1000 * 60 * 60 * 24)),
          hours: Math.floor(
            (remainingMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
          ),
          minutes: Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60)),
          seconds: Math.floor((remainingMs % (1000 * 60)) / 1000),
        };
      }
    },

    // 初始化剩余时间计算（启动定时器）
    initRemainingTime() {
      // 先清除已有定时器，避免重复
      if (this.timer) {
        clearInterval(this.timer);
      }
      // 立即计算一次
      this.calculateRemainingTime();
      // 每秒更新一次
      this.timer = setInterval(() => {
        this.calculateRemainingTime();
      }, 1000);
    },
    // 封装获取指定部分参数的方法
    getUrlParam() {
      const fullUrl = window.location.href;
      const urlParts = fullUrl.split("/");
      const lastPart = urlParts[urlParts.length - 1];
      const paramParts = lastPart.split("?");
      return paramParts[0];
    },
    // 搜索方法（搜索栏点击搜索/输入回车时触发）
    onSearch() {
      console.log("搜索关键词：", this.searchText);
      // 1. 重置分页状态（回到第一页，重新加载）
      this.page.page = 1;
      this.finished = false; // 重置为“未加载完成”
      this.list = []; // 清空原有列表数据

      // 2. 手动触发列表加载（调用 onLoad 方法）
      this.onLoad();
    },
    onLoad() {
      this.loading = true; //加载数据
      this.getPlayerList(); // 请求后端数据铺设界面
    },
    // 演员列表
    async getPlayerList() {
      let { page } = this;
      await playerList(this.activityId, this.page)
        .then((res) => {
          console.log("res:", res);
          if (res.data.code == 0) {
            console.log("res申请记录:", res);
            this.loading = false; // 加载关闭
            // 遍历 list 数组，修改 tbImageslList 中的 tbImage 键名为 url
            const listNew = res.data.data.records || [];
            this.list = this.list.concat(listNew);
            console.log("this.list :", this.list);

            this.$toast.clear(); //清除加载提示
            ++this.page.page;
            if (!res.data.data.records.length) {
              this.finished = true;
              return;
            }
          } else {
            this.finished = true;
            this.loading = false; // 加载关闭
          }
        })
        .catch((error) => {
          console.log("🚀 ~ getPlayerList ~ error:", error);
          //抛出错误
          this.error = true; //显示点击加载更多
          this.emptyState = true; // 区分初始渲染没数据
          this.refreshing = false;
          this.loading = false;
        });
    },
    async showPerPopup() {
      console.log("🚀 ~ showPerPopup ~ this.activityInfo:", this.activityInfo);
      try {
        // 1. 从本地存储读取关键信息（避免依赖this.currentUser，直接读本地更可靠）
        const localOpenId = localStorage.getItem("openId") || "";
        const localNickname = localStorage.getItem("nickname") || "";
        const localAvatar = localStorage.getItem("avatar") || "";

        // 2. 检查条件1：是否已登录（openId存在且非空）
        if (!localOpenId.trim()) {
          Toast.fail("请先登录后再投票");
          // 引导用户登录
          this.authorizeLogin();
          return;
        }

        // 3. 检查条件2：是否已获取完整userInfo（nickname和avatar非空）
        if (!localNickname.trim() || !localAvatar.trim()) {
          Toast.fail("请授权获取用户信息后再投票");
          // 引导用户授权获取用户信息
          await this.redirectToUserInfoAuth();
          // 授权后重新检查，满足则打开详情页
          const updatedNickname = localStorage.getItem("nickname") || "";
          if (updatedNickname.trim()) {
            this.openVote();
          }
          return;
        }

        // 4. 处理授权回调：若URL带授权code，先完成回调再打开页面
        const urlParams = this.getUrlCode();
        if (urlParams.code && urlParams.state === "userinfo") {
          await this.handleUserInfoCallback(urlParams.code);
          this.clearWechatCode();
          // 回调完成后打开详情页
          this.openVote();
          return;
        }
        this.openVote();
      } catch (error) {
        console.error("投票前校验失败:", error);
        Toast.fail("操作失败，请稍后重试");
      }
    },

    //返回首页正常关闭所有弹窗 接受值  关闭弹框
    handleChildEvent(data) {
      console.log("data:", data);
      this.passPerData.show = data.isShow;
      this.passPlayerData.show = data.isShow;
    },
    // 打开加票
    handleAddEvent(data, palyerItem) {
      console.log("🚀 ~ handleAddEvent ~ palyerItem:", palyerItem);
      console.log("🚀 ~ handleAddEvent ~ data:", data);
      this.passPlayerData.show = data.isShow;
      // this.passAddData.show = true;
      this.passAddData = {
        show: true,
        passCurrentConfig: this.currentConfig,
        playerItem: palyerItem, // 传递选手信息到详情页
      };
      console.log("🚀 ~ handleAddEvent ~ this.passAddData:", this.passAddData);
      console.log(
        "🚀 ~ handleAddEvent ~ his.passAddData.show:",
        this.passAddData.show
      );
    },
    // 回返详情页
    AddTicketsEvent(data) {
      console.log("data:", data);
      this.passAddData.show = data.isShow;
      this.passPlayerData.show = true;
      console.log("🚀 ~ AddTicketsEvent ~ !data.isShow:", !data.isShow);
    },

    // 投票 打开详情页
    // 投票：检查本地openId和userInfo后打开详情页
    async voteForPlayer(item) {
      try {
        // 1. 从本地存储读取关键信息（避免依赖this.currentUser，直接读本地更可靠）
        const localOpenId = localStorage.getItem("openId") || "";
        const localNickname = localStorage.getItem("nickname") || "";
        const localAvatar = localStorage.getItem("avatar") || "";

        // 2. 检查条件1：是否已登录（openId存在且非空）
        if (!localOpenId.trim()) {
          Toast.fail("请先登录后再投票");
          // 引导用户登录
          this.authorizeLogin();
          return;
        }

        // 3. 检查条件2：是否已获取完整userInfo（nickname和avatar非空）
        if (!localNickname.trim() || !localAvatar.trim()) {
          Toast.fail("请授权获取用户信息后再投票");
          // 引导用户授权获取用户信息
          await this.redirectToUserInfoAuth();
          // 授权后重新检查，满足则打开详情页
          const updatedNickname = localStorage.getItem("nickname") || "";
          if (updatedNickname.trim()) {
            this.openVoteDetails(item);
          }
          return;
        }

        // 4. 处理授权回调：若URL带授权code，先完成回调再打开页面
        const urlParams = this.getUrlCode();
        if (urlParams.code && urlParams.state === "userinfo") {
          await this.handleUserInfoCallback(urlParams.code);
          this.clearWechatCode();
          // 回调完成后打开详情页
          this.openVoteDetails(item);
          return;
        }
        console.log("🚀 ~ voteForPlayer ~ item:", item);
        // 5. 所有条件满足，打开投票详情页
        await this.fetchPlayerDetailAndOpen(item);
        this.openVoteDetails(item);
      } catch (error) {
        console.error("投票前校验失败:", error);
        Toast.fail("操作失败，请稍后重试");
      }
    },
    async fetchPlayerDetailAndOpen(item) {
      try {
        // 2. 核心：调用 getPlayerDetail 接口（仅传选手ID，不处理返回数据）
        await getPlayerDetail(item.id);
        console.log("getPlayerDetail 接口调用成功（选手ID：", item.id, "）");
      } catch (error) {
        // 3. 接口调用失败：仅打印日志+提示，不阻断打开详情页
        console.error("getPlayerDetail 接口调用失败:", error);
        Toast.fail({
          message: "详情接口请求失败",
          duration: 3000,
        });
      } finally {
        // 4. 无论接口成功/失败，都关闭加载提示并打开详情页
        Toast.clear();
      }
    },
    // 新增：复用打开投票详情页的逻辑（避免重复代码）
    openVoteDetails(item) {
      this.passPlayerData = {
        show: true,
        passCurrentConfig: this.currentConfig,
        playerItem: item, // 传递选手信息到详情页
      };
    },

    // this.passPerData = {
    //   show: true,
    //   buttonStyle: this.buttonStyle, // 将父组件的buttonStyle传递给子组件
    //   activeInfo: this.activityInfo, // 空对象兜底，避免 undefined
    // };
    // 我要报名
    openVote() {
      this.passPerData = {
        show: true,
        buttonStyle: this.buttonStyle, // 将父组件的buttonStyle传递给子组件
        activeInfo: this.activityInfo, // 空对象兜底，避免 undefined
      };
    },
    onSubmit() {
      this.$toast.loading({
        message: "提交中...",
        forbidClick: true,
      });

      setTimeout(() => {
        this.$toast.success("报名成功！");
        this.showSignUp = false;
        this.signUpForm = {
          name: "",
          phone: "",
          declaration: "",
        };
      }, 1500);
    },

    // 微信初始化微信配置
    initWechatJS() {
      let { nonceStr, timestamp, signature } = this;
      // 初始化微信JS-SDK配置
      wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来，若要查看传入的参数，可以在pc端打开，参数信息会通过log打出，仅在pc端时才会打印。
        appId: "wxc2d55befdf6585b2", //正式 必填，公众号的唯一标识
        // appId: "wxc639dc0a3c64a0ef", // 测试 必填，公众号的唯一标识
        timestamp: timestamp, // 必填，生成签名的时间戳
        nonceStr: nonceStr, // 必填，生成签名的随机串
        signature: signature, // 必填，签名
        jsApiList: [
          "getLocation",
          "openLocation",
          "chooseImage",
          "uploadImage",
        ], // 必须包含的API
      });
    },
    // 新增：清除 URL 中的 code 和 state 参数（避免重复使用过期 code）
    clearWechatCode() {
      let url = window.location.href;
      // 1. 先清除 code 参数（原有逻辑保留，扩展处理 & 位置）
      url = url.replace(/([?&])code=[^&]*&?/, (match, separator) => {
        const hasRemainingParams =
          match.includes("&") && match.indexOf("&") > match.indexOf("code=");
        return separator === "?" && !hasRemainingParams ? "" : "&";
      });

      // 2. 新增：清除 state 参数（同 code 逻辑，避免残留 state=STATE）
      url = url.replace(/([?&])state=[^&]*&?/, (match, separator) => {
        const hasRemainingParams =
          match.includes("&") && match.indexOf("&") > match.indexOf("state=");
        return separator === "?" && !hasRemainingParams ? "" : "&";
      });

      // 3. 移除末尾残留的 & 或 ?，避免 URL 格式错误（如 http://xxx.com/?& 变成 http://xxx.com/）
      url = url.replace(/[?&]$/, "");

      // 4. 处理微信锚点 #wechat_redirect 与前端路由冲突：保留前端路由的 #/，移除微信锚点
      if (url.includes("#wechat_redirect")) {
        // 情况1：URL 已有前端路由锚点（如 http://xxx.com/#/home#wechat_redirect）
        url = url.replace("#wechat_redirect", "");
        // 情况2：URL 无前端路由锚点（如 http://xxx.com#wechat_redirect）→ 补充为前端默认路由 #/
        if (!url.includes("#/")) {
          url += "#/";
        }
      }

      // 5. 最终修改 URL（不刷新页面）
      window.history.replaceState({}, document.title, url);
    },
    // 静默登录：使用新的投票服务
    async silentLoginWithCode(wechatCode) {
      if (!wechatCode) {
        console.error("微信code为空，终止登录");
        return;
      }

      // 防止重复登录
      if (this.isLoggingIn) {
        console.log("登录进行中，跳过重复请求");
        return;
      }

      this.isLoggingIn = true;

      try {
        Toast.loading({ message: "登录中...", forbidClick: true, duration: 0 });

        // 使用投票服务进行静默登录
        const loginResult = await voteService.silentLogin(wechatCode.trim());
        console.log(loginResult, "sdddsdddsds");
        if (loginResult.data.code == 0) {
          // 登录成功
          this.loginRetryCount = 0;
          const userData = loginResult.data.data;
          const userInfo = userData.userInfo || {};
          console.log("🚀 ~ silentLoginWithCode ~ userData:", userData);

          // 核心：完整存储 userInfo 到本地（openId + nickname + avatar）
          localStorage.setItem("token", userData.token || "");
          localStorage.setItem("openId", userInfo.openid || ""); // 存储openId
          localStorage.setItem("nickname", userInfo.nickname || ""); // 存储昵称
          localStorage.setItem("avatar", userInfo.headImgUrl || ""); // 存储头像

          // 更新当前用户状态
          this.currentUser = {
            openId: userInfo.openid || "",
            hasUserInfo: !!userInfo.nickname, // 有昵称即视为有完整userInfo
            nickname: userInfo.nickname || "",
            avatar: userInfo.headImgUrl || "",
          };

          // 检查用户信息完整性
          await this.checkUserInfoComplete();

          // 加载演员列表
          this.onLoad();
          Toast.success("登录成功");
        } else {
          // 登录失败
          const errorMsg = loginResult.msg || "登录失败";
          Toast.fail(errorMsg); // 新增：显示后端返回的错误信息
          this.loginRetryCount++;

          // 若为code相关错误，重新跳转授权页
          if (
            errorMsg.includes("code") ||
            errorMsg.includes("过期") ||
            errorMsg.includes("无效")
          ) {
            this.clearWechatCode();
            setTimeout(() => this.authorizeLogin(), 1500);
          }
        }
      } catch (error) {
        console.error("静默登录失败:", error);
        Toast.fail("网络异常，请检查网络后刷新");
        this.loginRetryCount = 0;
      } finally {
        // Toast.clear();
        this.clearWechatCode();
        this.isLoggingIn = false; // 重置登录状态
      }
    },
    // 微信网页授权（核心：获取 code 用于登录）
    authorizeLogin() {
      if (!this.isWechat()) {
        console.log("非微信环境，无需授权");
        return;
      }

      const hasToken = localStorage.getItem("token");
      if (hasToken) {
        console.log("已登录，无需重复授权");
        this.onLoad();
        return;
      }
      const { code, state } = this.getUrlCode();
      // const appId = "wxc639dc0a3c64a0ef"; // 测试环境
      const appId = "wxc2d55befdf6585b2"; //正式

      if (code && state === "STATE") {
        // 验证 state 防止非法回调
        // 关键修改：先清理 URL 参数，再处理登录（避免参数残留）
        this.clearWechatCode();
        // 用 code 调用登录接口
        this.silentLoginWithCode(code);
      } else if (!code) {
        // 无 code：跳转微信授权页（原有逻辑保留）
        const redirectUri = urlencode(window.location.href);
        const wxAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`;
        window.location.href = wxAuthUrl;
      }
    },
    //微信截取url中的code方法
    getUrlCode() {
      const url = location.search;
      const theRequest = {};
      if (url.indexOf("?") !== -1) {
        const str = url.substr(1);
        const strs = str.split("&");
        for (let i = 0; i < strs.length; i++) {
          theRequest[strs[i].split("=")[0]] = strs[i].split("=")[1];
        }
      }
      return theRequest;
    },
    //微信获取openid
    getOpenid(code) {
      axios
        .get(this.token, { params: { code } })
        .then((res) => {
          let token = res.data; //获取到token
          if (token) {
            localStorage.setItem("WExintoken", token);
            this.getUserApi();
          }
        })
        .catch((error) => {
          console.log(error);
        });
    },
    //微信获取用户信息
    getUserApi(openid, token) {
      axios
        .get(this.userInfo)
        .then((res) => {
          console.log("res:", res);
          if (res.status == 200) {
            this.$toast.success("授权成功");
            localStorage.setItem("imgUrl", res.data.headimgurl);
            localStorage.setItem("nickname", res.data.nickname);
          } else {
            this.$toast.fail("身份验证失败,请授权");
            localStorage.clear();
            window.location.href = "https://Jtfx.lzu.edu.cn";
          }
        })
        .catch((error) => {
          console.log("用户授权登录信息错误返回", error);
        });
    },

    // 检查用户信息完整性
    async checkUserInfoComplete() {
      if (!this.currentUser.openId) return;

      try {
        const userInfoCheck = await voteService.checkUserInfo(
          this.currentUser.openId
        );

        if (userInfoCheck.code === 0) {
          const userInfo = userInfoCheck.data;
          this.currentUser.hasUserInfo = userInfo.hasNickname;
          this.currentUser.nickname = userInfo.nickname || "";
          this.currentUser.avatar = userInfo.headImgUrl || "";

          // 存储用户信息
          if (userInfo.hasNickname) {
            localStorage.setItem("nickname", userInfo.nickname);
            localStorage.setItem("avatar", userInfo.headImgUrl);
          }
        }
      } catch (error) {
        console.error("检查用户信息失败:", error);
      }
    },

    // 引导用户授权获取详细信息
    async redirectToUserInfoAuth() {
      try {
        const currentUrl = window.location.href;
        await voteService.redirectToUserInfoAuth(currentUrl);
      } catch (error) {
        console.error("跳转授权页面失败:", error);
        Toast.fail("授权失败，请稍后重试");
      }
    },

    // 处理用户信息授权回调
    async handleUserInfoCallback(code) {
      try {
        Toast.loading({ message: "获取用户信息中...", forbidClick: true });
        const userInfoResult = await voteService.getUserDetailInfo(code);

        if (userInfoResult.code === 0) {
          const userDetail = userInfoResult.data;
          // 核心：更新本地存储的userInfo（覆盖旧值）
          localStorage.setItem("nickname", userDetail.nickname || "");
          localStorage.setItem("avatar", userDetail.headImgUrl || "");

          // 更新当前用户状态
          this.currentUser.hasUserInfo = true;
          this.currentUser.nickname = userDetail.nickname || "";
          this.currentUser.avatar = userDetail.headImgUrl || "";

          Toast.success("用户信息获取成功");
        } else {
          Toast.fail(userInfoResult.msg || "获取用户信息失败");
        }
      } catch (error) {
        console.error("获取用户信息失败:", error);
        Toast.fail("获取用户信息失败");
      } finally {
        Toast.clear();
      }
    },

    // 投票前检查用户信息
    async checkUserInfoBeforeVote() {
      console.log(
        "🚀 ~ checkUserInfoBeforeVote ~ this.currentUser.openId:",
        this.currentUser.openId
      );
      if (!this.currentUser.openId) {
        Toast.fail("请先登录");
        return false;
      }

      if (!this.currentUser.hasUserInfo) {
        // 用户信息不完整，引导授权
        Dialog.confirm({
          title: "需要获取您的微信信息",
          message: "为了更好的投票体验，需要获取您的微信昵称和头像。是否授权？",
          confirmButtonText: "授权",
          cancelButtonText: "取消",
        })
          .then(() => {
            this.redirectToUserInfoAuth();
          })
          .catch(() => {
            // 用户取消授权
          });
        return false;
      }

      return true;
    },

    showWechatTip() {
      // 禁止页面滚动（防止用户看到隐藏的内容）
      document.body.style.overflow = "hidden";

      // 关键修改：confirmButtonText设为空字符串，隐藏按钮文本
      Dialog.alert({
        title: "请使用微信内置浏览器打开",
        message:
          "本页面需依赖微信授权及投票功能，仅支持微信内置浏览器访问。\n\n操作指引：\n1. 复制当前链接\n2. 打开微信粘贴链接访问\n3. 或通过微信扫描对应二维码",
        //confirmButtonText: "我知道了", // 1. 按钮文本设为空（默认“确定”消失）
        confirmButtonColor: this.commonColor,
        closeOnClickOverlay: true, // 点击遮罩不关闭
        closeOnPopstate: false, // 页面回退不关闭
        className: "no-button-dialog", // 2. 新增自定义类名，用于隐藏按钮容器
      }).then(() => {
        // 按钮已隐藏，此回调不会触发，可保留或删除（不影响功能）
        this.showWechatTip();
      });
    },
  },
};
</script>

<style lang="less" scoped>
/* 背景音乐控制按钮：左上角悬浮 */
.music-control {
  position: fixed;
  top: 55px; /* 距离顶部 */
  right: 20px; /* 距离左侧 */
  z-index: 9999; /* 确保在所有内容上层 */
  background-color: rgba(0, 0, 0, 0.5); /* 半透黑背景，提升辨识度 */
  border-radius: 50%; /* 圆形外观 */
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer; /* 鼠标悬浮为手型，提示可点击 */
}
// 公告栏样式（固定在页面顶端，层级高）
.custom-notice-bar {
  /* 1. 改为 fixed 定位，相对于视口固定，滚动页面不移动 */
  position: fixed;
  top: 0; /* 贴紧页面顶部 */
  left: 0;
  right: 0; /* 左右贴边，实现全屏宽度 */
  z-index: 9998; /* 层级：低于全屏加载层(9999)，高于其他内容 */
  display: flex;
  align-items: center;
  background: rgba(255, 153, 0, 0.596); /* 加深背景透明度，提升辨识度 */
  height: 32px; /* 固定高度，确保下方内容 padding 匹配 */
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); /* 增加阴影，突出悬浮感 */
  // backdrop-filter: blur(4px); /* 背景模糊，提升视觉层次（兼容现代浏览器） */
}

/* 2. 穿透调整 van-notice-bar 组件样式，确保内容居中且不溢出 */
:deep(.van-notice-bar) {
  flex: 1; /* 占满公告栏宽度 */
  height: 100%; /* 与公告栏高度一致 */
  line-height: 32px; /* 垂直居中 */
  color: #fff !important; /* 强制文字白色，避免组件默认色干扰 */
  background: transparent !important; /* 清除组件默认背景，继承父容器背景 */
}

/* 3. 给下方内容添加顶部内边距，避免被公告遮挡 */
.page-content {
  /*  padding-top: 0; 移除原有可能的 padding */
  padding-top: 32px; /* 高度 = 公告栏高度，刚好避开公告 */
  position: relative; /* 保留原有定位，不影响其他子元素 */
}

/* 7. 全屏加载层样式：覆盖整个屏幕，居中显示 */
.fullscreen-loading {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: #fff; /* 白色背景覆盖默认样式 */
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 9999; /* 最高优先级，防止被覆盖 */
  transition: opacity 0.3s ease;

  .loading-text {
    margin-top: 16px;
    font-size: 14px;
    color: #666;
  }
}

// 关键：隐藏按钮所在的容器（van-dialog__footer）
:deep(.no-button-dialog) {
  // 隐藏按钮容器（彻底去掉按钮区域）
  .van-dialog__footer {
    display: none !important;
  }
  // 优化内容区布局：去除按钮后增加底部内边距，避免内容贴底
  .van-dialog__content {
    padding-bottom: 20px !important;
  }
  // 可选：调整提示框宽度（适配手机屏幕）
  width: 85% !important;
  max-width: 350px !important;
}

// 原有“非微信提示框基础样式”保留（确保文字换行、可读性）
:deep(.wechat-tip-dialog) {
  .van-dialog__title {
    font-size: 18px;
    font-weight: 600;
    color: #333;
    margin-bottom: 10px;
    text-align: center; // 可选：标题居中
  }
  .van-dialog__content {
    font-size: 14px;
    color: #666;
    line-height: 1.8;
    white-space: pre-line; // 支持\n换行
    margin: 15px 0;
    text-align: left; // 操作指引左对齐，更易读
  }
}
// 6. 新增：非微信提示框样式（确保内容换行、字体清晰）
:deep(.wechat-tip-dialog) {
  .van-dialog__title {
    font-size: 18px;
    font-weight: 600;
    color: #333;
    margin-bottom: 10px;
  }
  .van-dialog__content {
    font-size: 14px;
    color: #666;
    line-height: 1.8; // 增加行高，提升可读性
    white-space: pre-line; // 支持\n换行（关键）
    margin: 15px 0;
  }
  .van-dialog__confirm {
    font-size: 16px;
    font-weight: 500;
    padding: 8px 24px;
  }
}

// 原有样式保留（vote-h5-container、header-decoration、main-content等）
.vote-h5-container {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #fff;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
//  position: relative; /* 核心：子容器 absolute 会相对于此父容器定位 */
/* 头部样式 */
.header-decoration {
  padding: 10px 0 10px;
  text-align: center;
  position: relative;
  flex-shrink: 0;

  .activity-title {
    font-size: 16px;
    font-weight: 700;
  }
}

/* 中间内容区 */
.main-content {
  flex: 1; /* 占满剩余高度（父容器必须是flex布局） */
  overflow-y: auto; /* 允许垂直滚动 */
  padding-bottom: 30px; /* 底部留空，避免被其他元素遮挡 */
  -webkit-overflow-scrolling: touch;
}
.player-list-section {
  width: 100%;
  padding: 0 10px;
  /* 禁止设置固定高度，如 height: 100px; */
}
.my-swipe {
  margin: 10px;
  border-radius: 8px;
  overflow: hidden;
  height: 200px;

  .carousel-slide {
    width: 100%;
    height: 100%;

    .slide-image {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    .slide-content {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 20px;
      color: #333;
    }
  }
}

/* 信息统计 */
.infoCount {
  margin: 10px;

  .stats {
    border-radius: 8px 8px 0px 0px;
    color: #fff;
    display: flex;
    justify-content: space-around;
    padding: 8px 0px;

    .stat-item {
      text-align: center;
    }

    .stat-value {
      font-size: 18px;
      font-weight: bold;
    }
  }

  .countdown {
    border-radius: 0px 0px 8px 8px;
    text-align: center;
    background-color: #fff;
    padding: 11px 0px;
  }
}

.time-info {
  margin: 10px;
  text-align: left;
  background-color: #fff;
  padding: 8px 10px;

  .time_item {
    width: 100%;
    margin-bottom: 8px;
  }
}

.BaoMing_Btn {
  margin: 10px 10px 15px 10px;
}

.search-container {
  padding: 0 10px 10px;

  /deep/ .van-search__action {
    color: white;
    padding: 0 10px;
    border-radius: 0 4px 4px 0;
  }
}

.van-search__content {
  background-color: #fff !important;
}

.van-search {
  padding: 0px;
  height: 50px;
}

.search_btn {
  width: 50px;
  text-align: center;
  padding: 0px 10px;
}

/* 演员列表相关样式 */
.player-list-section {
  padding: 0 10px 10px;

  .section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;

    .section-title {
      font-size: 17px;
      font-weight: 600;
      margin: 0;
      display: flex;
      align-items: center;

      &::before {
        content: "";
        display: inline-block;
        width: 4px;
        height: 16px;
        border-radius: 2px;
        margin-right: 8px;
        background: var(--title-color);
      }
    }

    /deep/ .van-dropdown-menu {
      height: auto;

      .van-dropdown-menu__title {
        font-size: 13px;
        padding: 5px 12px;
      }
    }
  }
}

/* 演员列表项样式 */
.players-container {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.grid-mode {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  .player-item {
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 18px 10px;
    position: relative;

    .player-rank {
      position: absolute;
      top: 10px;
      right: 10px;
    }

    .player-info {
      align-items: center;
      margin-top: 10px;
    }

    .van-button {
      margin-top: 10px;
      width: 90%;
    }
  }
}

/* 列表模式 */

.players-container.list-mode .player-item {
  flex-direction: row;
  padding: 15px;
  align-items: center;
}

.players-container.list-mode .player-avatar {
  width: 60px;
  height: 60px;
}

.players-container.list-mode .player-info {
  flex: 1;
  margin-left: 15px;
  align-items: flex-start;
}

.players-container.list-mode .player-desc {
  display: none;
}

// .players-container.list-mode .vote-btn {
//   margin-left: auto;
//   min-width: 80px;
// }

/* 卡片模式 */

.players-container.card-mode .player-item {
  flex-direction: column;
  align-items: flex-start;
  padding: 20px;
}

.players-container.card-mode .player-header {
  display: flex;
  width: 100%;
  align-items: center;
  margin-bottom: 15px;
}

.players-container.card-mode .player-info {
  align-items: flex-start;
  width: 100%;
}

.players-container.card-mode .player-desc {
  margin: 10px 0;
  line-height: 1.6;
}

.players-container.card-mode .van-button {
  width: 100%;
  margin-top: 15px;
}
/* 演员项通用样式 */
.player-item {
  background: white;
  border-radius: 16px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
  display: flex;
  transition: transform 0.3s, box-shadow 0.3s;
  position: relative;

  &:active {
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
  }
}

.player-rank {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #f5f5f5;
  color: #666;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  z-index: 1;
  margin-right: 5px;

  &.first {
    background: linear-gradient(135deg, #ffd700 0%, #ffa500 100%);
    color: white;
    box-shadow: 0 2px 8px rgba(255, 165, 0, 0.3);
  }

  &.second {
    background: linear-gradient(135deg, #e0e0e0 0%, #a9a9a9 100%);
    color: white;
    box-shadow: 0 2px 8px rgba(169, 169, 169, 0.3);
  }

  &.third {
    background: linear-gradient(135deg, #cd7f32 0%, #d2691e 100%);
    color: white;
    box-shadow: 0 2px 8px rgba(210, 105, 30, 0.3);
  }
}

.player-avatar-container {
  position: relative;

  .online-indicator {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 18px;
    height: 18px;
    background: #4cd964;
    border: 2px solid white;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  }
}

.player-info {
  display: flex;
  flex-direction: column;
  flex: 1;

  .player-name {
    font-size: 16px;
    font-weight: 600;
    color: #333;
  }

  .player-votes {
    display: flex;
    align-items: center;
    font-size: 14px;
    color: #ff4d4f;
    margin-top: 5px;

    .van-icon {
      margin-right: 5px;
    }
  }
}

/* 报名表单样式 */
.signup-form {
  padding: 20px;

  h3 {
    text-align: center;
    margin-bottom: 20px;
    color: var(--title-color);
  }

  /deep/ .van-button {
    margin-top: 20px;
  }
}

/* 空状态样式 */
.empty-state {
  padding: 40px 0;
  text-align: center;
}

/* 网络异常状态样式 */
.network-error-state {
  padding: 40px 20px;
  text-align: center;
  background-color: #fff8f8;
  border-radius: 8px;
  margin: 10px;

  .error-text {
    margin: 10px 0;
    color: #ff4d4f;
    font-size: 14px;
  }
}

/* 可选：加载中状态覆盖层（防止下拉时重复触发） */
:deep(.van-list__loading) {
  padding: 15px 0;
  color: #969799;
  font-size: 14px;
  line-height: 30px;
  text-align: center;
}

/* 漂浮物样式 - 核心部分 */

.floating-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  /* 确保漂浮物不影响交互 */
  z-index: 10;
  /* 确保漂浮物在内容上方显示 */
  overflow: hidden;
  /* 隐藏容器外的漂浮物 */
  height: 100vh;
}

.floating-item {
  position: absolute;
  will-change: transform, opacity;
  /* 优化动画性能 */
  animation-name: floatDown;
  animation-timing-function: cubic-bezier(0.34, 0.56, 0.64, 0.5);
  /* 缓动函数，使下落更自然 */
  animation-iteration-count: infinite;
  /* 无限循环 */
  animation-fill-mode: both;
}

.floating-img {
  width: 40px;
  height: 40px;
  object-fit: contain;
  opacity: 0.8;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
}

/* 漂浮物下落动画 - 从顶部到底部完整路径 */

@keyframes floatDown {
  0% {
    /* 开始位置：容器顶部外 */
    transform: translateY(-10px) translateX(0) scale(var(--size, 1));
    opacity: 0;
  }
  10% {
    /* 进入容器 */
    transform: translateY(0) translateX(0) scale(var(--size, 1));
    opacity: 0.8;
  }
  25% {
    /* 第一阶段：轻微右移 */
    transform: translateY(calc(100vh - 100px)) translateX(var(--swing-range))
      scale(var(--size, 1));
    opacity: 0.9;
  }
  50% {
    /* 中间阶段：轻微左移 */
    transform: translateY(calc(100vh - 300px))
      translateX(calc(-1 * var(--swing-range) / 2)) scale(var(--size, 1));
    opacity: 1;
  }
  75% {
    /* 第三阶段：再次右移 */
    transform: translateY(calc(100vh - 500px))
      translateX(var(--swing-range) / 2) scale(var(--size, 1));
    opacity: 0.9;
  }
  90% {
    /* 即将到达底部 */
    transform: translateY(calc(100vh - 600px)) translateX(0)
      scale(var(--size, 1));
    opacity: 0.8;
  }
  100% {
    /* 到达底部并移出容器 */
    transform: translateY(calc(100vh + 100px)) translateX(0)
      scale(var(--size, 1));
    opacity: 0;
  }
}
</style>
